package com.zksite.nator.client.server.handler;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;

@Sharable
public abstract class ConnectionWatchdog extends ChannelInboundHandlerAdapter implements TimerTask {

	private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionWatchdog.class);

	private final Bootstrap bootstrap;
	private final Timer timer;
	private final int port;

	private final String host;

	private volatile boolean reconnect = true;
	private int attempts;

	public abstract ChannelHandler[] handlers();

	public ConnectionWatchdog(Bootstrap bootstrap, Timer timer, int port, String host, boolean reconnect) {
		this.bootstrap = bootstrap;
		this.timer = timer;
		this.port = port;
		this.host = host;
		this.reconnect = reconnect;
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		attempts = 0;
		ctx.fireChannelActive();
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		LOGGER.info("已与服务器断开链接");
		if (reconnect) {
			if (attempts < 12) {
				LOGGER.info("准备重连，已重连次数：{}", attempts);
				attempts++;
				// 重连的间隔时间会越来越长
				int timeout = 2 << attempts;
				timer.newTimeout(this, timeout, TimeUnit.MILLISECONDS);
			} else {
				LOGGER.info("已超出最大重连次数.应用关闭中");
				Runtime.getRuntime().exit(0);
			}
		}
		ctx.fireChannelInactive();
	}

	@Override
	public void run(Timeout timeout) throws Exception {
		ChannelFuture future;
		// bootstrap已经初始化好了，只需要将handler填入就可以了
		synchronized (bootstrap) {
			bootstrap.handler(new ChannelInitializer<Channel>() {

				@Override
				protected void initChannel(Channel ch) throws Exception {

					ch.pipeline().addLast(handlers());
				}
			});
			future = bootstrap.connect(host, port);
		}
		// future对象
		future.addListener(new ChannelFutureListener() {

			public void operationComplete(ChannelFuture f) throws Exception {
				boolean succeed = f.isSuccess();

				// 如果重连失败，则调用ChannelInactive方法，再次出发重连事件，一直尝试12次，如果失败则不再重连
				if (!succeed) {
					LOGGER.info("自动重连失败");
					f.channel().pipeline().fireChannelInactive();
				} else {
					LOGGER.info("自动重连成功");
				}
			}
		});
	}

}
